package graph.drawingshapes
{
	import flash.display.DisplayObject;
	import flash.events.Event;
	import flash.events.MouseEvent;
	import flash.geom.Point;
	import flash.geom.Rectangle;
	
	import graph.drawingshapes.shapes.RectangleShape;
	import mx.events.PropertyChangeEvent;
	import mx.managers.CursorManager;
	
	/**
	 * Singleton class represents selection shape with markers to drag and resize binded objects.
	 * @author Андрій
	 * 
	 */	
	public class ResizeTool extends RectangleShape
	{
		public static const NAME:String = "ResizeTool";
		public override function get getName():String
		{
			return NAME;
		}
		
		public static const MARKER_N:String = "N";
		public static const MARKER_NE:String = "NE";
		public static const MARKER_E:String = "E";
		public static const MARKER_SE:String = "SE";
		public static const MARKER_S:String = "S";
		public static const MARKER_SW:String = "SW";
		public static const MARKER_W:String = "W";
		public static const MARKER_NW:String = "NW";
		
		[Embed(source="assets/cursors/s-resize.gif")]
		private var nsCursor:Class;
		[Embed(source="assets/cursors/w-resize.gif")]
		private var ewCursor:Class;
		[Embed(source="assets/cursors/sw-resize.gif")]
		private var swCursor:Class;
		[Embed(source="assets/cursors/se-resize.gif")]
		private var seCursor:Class;
		
		[Bindable]
		private var _markers:Array;
		[Bindable]
		protected virtual function get markers():Array
		{
			return _markers; 
		}
		protected virtual function set markers(value:Array):void
		{
			_markers = value;
		}
		
		[Bindable]
		public function get middleWidth():Number
		{
			var result:Number = this.width / 2;
			return result;
		}
		public function set middleWidth(value:Number):void
		{
			this.width = value*2;
		}
		
		[Bindable]
		public function get middleHeight():Number
		{
			var result:Number = this.height / 2;
			return result;
		}
		public function set middleHeight(value:Number):void
		{
			this.height = value*2;
		}
		
		[Bindable]
		protected var _boundsRect:Rectangle = null;
		[Bindable]
		public function get boundsRect():Rectangle
		{
			return this._boundsRect;
		}
		public function set boundsRect(value:Rectangle):void
		{
			this._boundsRect = value;
		}
		
		private static var _instance:ResizeTool = null;
		public static function get instance():ResizeTool
		{
			if (_instance == null)
			{
				_instance = new ResizeTool(new PrivateClass()); 
			}
			return _instance;
		}			
		
		public function ResizeTool(privateClass:PrivateClass, x:Number=0, y:Number=0, width:Number=0, height:Number=0, boundsRect:Rectangle = null)
		{
			super(x, y, width, height);
			initStyle();
			initMarkers();
			this.boundsRect = boundsRect;
		}
		
		public function init(x:Number=DefaultX, y:Number=DefaultY, width:Number=DefaultWidth, height:Number=DefaultHeight, boundsRect:Rectangle = null):void
		{
			this.x = x;
			this.y = y;
			this.width = width;
			this.height = height;
			this.boundsRect = boundsRect;
		}
		
		private function initStyle():void
		{
			this.outline = new Outline(0x0000CC, 0.3, 1.0);
			this.fill = new NoFill();
		}
		
		private function initMarkers():void
		{
			// create markers
			
			this.markers = new Array(8);
			this.markers[MARKER_N] = new ResizeMarker(MARKER_N, middleWidth, 0);
			this.markers[MARKER_NE] = new ResizeMarker(MARKER_NE, width, 0);
			this.markers[MARKER_E] = new ResizeMarker(MARKER_E, width, middleHeight);
			this.markers[MARKER_SE] = new ResizeMarker(MARKER_SE, width, height);
			this.markers[MARKER_S] = new ResizeMarker(MARKER_S, middleWidth, height);
			this.markers[MARKER_SW] = new ResizeMarker(MARKER_SW, 0, height);
			this.markers[MARKER_W] = new ResizeMarker(MARKER_W, 0, middleHeight);
			this.markers[MARKER_NW] = new ResizeMarker(MARKER_NW, 0,0);
			
			for each (var marker:DisplayObject in this.markers)
			{
				this.addChild(marker);
				(marker as ResizeMarker).addEventListener(PropertyChangeEvent.PROPERTY_CHANGE, onMarkerPropertyChange);
				(marker as ResizeMarker).addEventListener(MouseEvent.MOUSE_OVER, onMarkerMouseOver);
				(marker as ResizeMarker).addEventListener(MouseEvent.MOUSE_OUT, onMarkerMouseOut);
				(marker as ResizeMarker).addEventListener(MouseEvent.MOUSE_DOWN, onMarkerMouseDown);
				(marker as ResizeMarker).addEventListener(MouseEvent.MOUSE_UP, onMarkerMouseUp);				
			}
		}
		
		protected virtual function onMarkerMouseDown(event:MouseEvent):void
		{
			// if target is SelectionMarker
			if (!(event.target is ResizeMarker)) return;
			
			// begin marker dragging			
			var marker:ResizeMarker = event.target as ResizeMarker;
			
			var markerBoundsRect:Rectangle = null;
			if (this.boundsRect != null)
			{
				var topLeft:Point = marker.globalToLocal(this.boundsRect.topLeft);
				markerBoundsRect = new Rectangle(topLeft.x, topLeft.y, this.boundsRect.width, this.boundsRect.height);						
			}
			
			marker.beginDrag(markerBoundsRect);
			
			// add mouse up event handler for parent object
			if (this.parent != null)
			{
				this.parent.addEventListener(MouseEvent.MOUSE_UP, onMarkerMouseUp);
			}
		}
		
		protected virtual function onMarkerMouseOver(event:MouseEvent):void
		{
			// if target is SelectionMarker
			if (!(event.target is ResizeMarker)) return;
			
			//change mouse cursor
			var marker:ResizeMarker = event.target as ResizeMarker;
			
			setCursor(marker.ID);
		}
		
		protected virtual function onMarkerMouseOut(event:MouseEvent):void
		{
			// if target is SelectionMarker
			if (!(event.target is ResizeMarker)) return;
			
			//change mouse cursor
			var marker:ResizeMarker = event.target as ResizeMarker;
			
			// in case marker is still dragging do not change cursor
			if (marker.isDragged) return;
			
			removeCursor();
		}
		
		private function removeCursor():void
		{
			CursorManager.removeAllCursors();
		}
		
		private function setCursor(id:String):void
		{
			var cursorClass:Class = null;
			switch(id)
			{
				case MARKER_N:
				case MARKER_S:
					cursorClass = nsCursor;
					break;
				case MARKER_W:
				case MARKER_E:
					cursorClass = ewCursor;
					break;
				case MARKER_NW:
				case MARKER_SE:
					cursorClass = seCursor;
					break;
				case MARKER_NE:
				case MARKER_SW:
					cursorClass = swCursor;
					break;
				default:
					cursorClass = null;
					break;
			}
			
			if (cursorClass != null) 
			{
				CursorManager.setCursor(cursorClass,2,-20,-20);
			}
		
			
		}
		
		protected virtual function onMarkerMouseUp(event:MouseEvent):void
		{
			for each (var marker:ResizeMarker in this.markers)
			{
				if (marker.isDragged)
				{
					marker.endDrag();
				}
			}
			
			removeCursor();
			
			if (this.parent != null && this.parent.hasEventListener(MouseEvent.MOUSE_UP))
			{
				this.parent.removeEventListener(MouseEvent.MOUSE_UP, onMarkerMouseUp);
			}
		}
		
		protected override function initHandlers():void
		{
			super.initHandlers();
			this.addEventListener(Event.ENTER_FRAME, onEnterFrame);
		}
		
		protected virtual function onEnterFrame(event:Event):void
		{
			// Nothing to do with no 
			if (this.parent == null) return;
			for each (var obj:Object in this.markers)
			{
				// do not respond to not Selection marker objects
				if (!(obj is ResizeMarker)) continue;
				
				var marker:ResizeMarker = obj as ResizeMarker;
				// do not respond tp not fragged markers
				if (!marker.isDragged) continue;
				var dx:Number = 0;
				var dy:Number = 0;
				var dw:Number = 0;
				var dh:Number = 0;
				
				var halfMarkerWidth:Number = marker.width/2;
				var halfMarkerHeight:Number = marker.height/2;
				
				var canvasMouseX:Number = this.parent.mouseX;
				var canvasMouseY:Number = this.parent.mouseY;
				
				var mouseDX:Number = marker.mouseX;
				var mouseDY:Number = marker.mouseY;
				
				canvasMouseX -= mouseDX;
				canvasMouseY -= mouseDY;
				
				switch (marker.ID)
				{
					case MARKER_N:
						dh = this.y - canvasMouseY;
						dy = canvasMouseY - this.y;
						marker.y -= dy;
						break;
					case MARKER_NE:
						dw = canvasMouseX - this.x - width;
						dh = this.y - canvasMouseY;
						dy = canvasMouseY - this.y;
						marker.y -= dy;
						break;
					case MARKER_E:
						dw = canvasMouseX - this.x - width;
						break;
					case MARKER_SE:
						dw = canvasMouseX - this.x - width;
						dh = canvasMouseY - this.y - height;
						break;
					case MARKER_S:
						dh = canvasMouseY - this.y - height;
						break;
					case MARKER_SW:
						dh = canvasMouseY - this.y - height;
						dw = this.x - canvasMouseX;
						dx = canvasMouseX - this.x;
						marker.x -= dx;
						break;
					case MARKER_W:
						dw = this.x - canvasMouseX;
						dx = canvasMouseX - this.x;
						marker.x -= dx;
						break;
					case MARKER_NW:
						dw = this.x - canvasMouseX;
						dx = canvasMouseX - this.x;
						dh = this.y - canvasMouseY;
						dy = canvasMouseY - this.y;
						marker.x -= dx;
						marker.y -= dy;
						break;
				}
				
				this.x += dx;
				this.y += dy;
				this.width += dw;
				this.height += dh;
			}
						
			repositionMarkers();
		}

		protected virtual function repositionMarkers():void
		{
			if (this.markers == null || this.markers.length < 8) return;
			
			for each (var obj:Object in this.markers)
			{
				// do nothing with not marker object
				if (!(obj is ResizeMarker)) continue;
				
				var marker:ResizeMarker = obj as ResizeMarker;
				
				// reposition apropriate markers' coordinates of dragged markers of strict directions 
				if (marker.isDragged)
				{
					switch(marker.ID)
					{
						case MARKER_N:
							marker.x = middleWidth;
							break;
						case MARKER_E:
							marker.y = middleHeight;
							break;
						case MARKER_S:
							marker.x = middleWidth;
							break;						
						case MARKER_W:
							marker.y = middleHeight;
							break;
					}
				}
				else
				{ 
					// perform unique repositioning for different markers
					switch(marker.ID)
					{
						case MARKER_N:
							marker.x = middleWidth;
							marker.y = 0;
							break;
						case MARKER_NE:
							marker.x = width;
							marker.y = 0;
							break;
						case MARKER_E:
							marker.x = width;
							marker.y = middleHeight;
							break;
						case MARKER_SE:
							marker.x = width;
							marker.y = height;
							break;
						case MARKER_S:
							marker.x = middleWidth;
							marker.y = height;
							break;
						case MARKER_SW:
							marker.x = 0;
							marker.y = height;
							break;
						case MARKER_W:
							marker.x = 0;
							marker.y = middleHeight;
							break;
						case MARKER_NW:
							marker.x = 0;
							marker.y = 0;
							break;
					}
				 }
			}
		}
		
		protected function onMarkerPropertyChange(event:PropertyChangeEvent):void
		{
			
		}
		
		protected override function onProperyChangeEvent(event:PropertyChangeEvent):void
		{
			// nothing to do here, beacuse we don't want to register property change for selectionshape.	
		}
		
		protected override function onRedrawEvent(event:RedrawEvent):void
		{
			super.onRedrawEvent(event);
			repositionMarkers();
		}		
	}
}

class PrivateClass
{
	public function PrivateClass()
	{
		
	}
}